/*LICENSE_BEGIN
 * ******************************************************************
 * All original parts of SoccerScope2003 have copyright (2003) by
 * Team YowAI, represented by Mr. Shuhei Shiota
 * The Department of Creative Informatics
 * The Graduate School of Information Science and Technology
 * The University of Tokyo
 * 
 * Additional classes (except libraries in /lib) were developed by 
 * the Dainamite Robocup-Team of the DAI-Labor, TU-Berlin 
 * (www.dai-labor.de)
 * 
 * Extended/new classes/packages are:
 *   soccerscope.analyzer.LogfileAnalyzer
 *   soccerscope.daistatistic.*
 *   soccerscope.daistatistic.charts.*
 *   soccerscope.daistatistic.tools.*
 *   soccerscope.db.*
 *   soccerscope.dialog.*
 *   soccerscope.model.PlayerWorldModel
 *   soccerscope.model.SimplePlayer
 *   soccerscope.net.AgentConnector
 *   soccerscope.training.*
 *   soccerscope.training.conditions.*
 * ******************************************************************
LICENSE_END*/
package soccerscope.model;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Hashtable;

import robocup.component.AbstractAgent;
import soccerscope.model.agentplan.AgentPlan;
import soccerscope.model.agentplan.AgentPlanCommand;
import soccerscope.model.agentplan.CommentDB;
import soccerscope.model.agentplan.Context;
import soccerscope.model.agentplan.ParseException;
import soccerscope.training.TrainingModel;

public class WorldModel implements Serializable {

    /**
     * id for serialization. 
     */
    private static final long serialVersionUID = 3256726186435426101L;

	private static WorldModel instance = new WorldModel();

	public static SceneSet sceneSet;

	// which player is selected
	private boolean[] isSelected;

	private Hashtable<Integer, ArrayList> agentplanMap;

	// TrainingModel is a global thing:
	public TrainingModel trainModel = null;

	public AbstractAgent trainer = null;

	// indicates, if training should take place or
	// not, can be altered by the user starting the
	// trainer
	private boolean trainingMode = false;

	public static WorldModel getInstance() {
		return instance;
	}

	private WorldModel() {
		sceneSet = new SceneSet(ClientScene.class);
		this.isSelected = new boolean[Param.MAX_PLAYER];

		this.agentplanMap = new Hashtable<Integer, ArrayList>();
	}

	public void clear() {
		sceneSet.clear();
		this.agentplanMap.clear();
		CommentDB.clear();
		// Runtime runtime = Runtime.getRuntime();
		// System.out.println("before gc");
		// System.out.println("free: " + runtime.freeMemory());
		// System.out.println("total: " + runtime.totalMemory());
		System.gc();
		// System.out.println("after gc");
		// System.out.println("free: " + runtime.freeMemory());
		// System.out.println("total: " + runtime.totalMemory());
	}

	public void clearAgentPlan() {
		this.agentplanMap.clear();
		CommentDB.clear();
	}

	public static SceneSet getSceneSet() {
		return sceneSet;
	}

	public void makeAgentPlan(String filename) {
		try {
			BufferedReader in = new BufferedReader(new FileReader(filename));
			AgentPlan plan = new AgentPlan();
			for (;;) {
				String str = in.readLine();
				if (str == null)
					break;
				try {
					plan.parse(new Context(str));
					int time = plan.getTime();
					int unum = plan.getUnum();
					Integer key = createKey(time, unum);
					AgentPlanCommand command = plan.getAgentPlanCommand();

					if (command.getType() == AgentPlanCommand.COMMENT) {
						CommentDB.putComment(command.getComment(), time,
								unum - 1);
					} else if (this.agentplanMap.containsKey(key)) {
						ArrayList<AgentPlanCommand> list = this.agentplanMap
								.get(key);
						list.add(command);
					} else {
						ArrayList<AgentPlanCommand> list = new ArrayList<AgentPlanCommand>();
						list.add(command);
						this.agentplanMap.put(key, list);
					}
				} catch (ParseException e) {
					System.err.println(e);
					System.err.println(str);
				}
			}
			in.close();
		} catch (IOException ie) {
			System.err.println(ie);
		}
	}

	public Integer createKey(int time, int unum) {
		return new Integer(time * 100 + unum);
	}

	public boolean hasAgentPlan(int time, int unum) {
		return this.agentplanMap.containsKey(createKey(time, unum));
	}

	public boolean hasAgentPlan(Integer key) {
		return this.agentplanMap.containsKey(key);
	}

	public ArrayList getAgentPlan(int time, int unum) {
		Integer key = createKey(time, unum);
		if (this.agentplanMap.containsKey(key)) {
			return this.agentplanMap.get(key);
		}
		return null;
	}

	public ArrayList getAgentPlan(Integer key) {
		if (this.agentplanMap.containsKey(key)) {
			return this.agentplanMap.get(key);
		}
		return null;
	}

	public boolean[] getIsSelected() {
		return this.isSelected;
	}

	/**
	 * This method executes a training-step, if training is possible. However,
	 * since the monitor should already be connected to the server (when this
	 * method was called from SceneSetMaker.run() in Mode
	 * SceneBuilder.SHOW_MODE), there has to be checked, if a scenario and a
	 * trainer exists.
	 * 
	 * @param right
	 *            the right (red) team
	 * @param left
	 *            the left (yellow) team
	 */
	public void executeTraining() {
		if (this.trainModel == null)
			return;
		if (this.trainer == null)
			return;

		if (!this.trainModel.isPrepared()) {
			this.trainModel.prepareTrainingScenario();
		} else {
			if (!this.trainModel.isStarted())
				this.trainModel.startTrainingScenario();
			else {
				ClientScene actual = this.getSceneSet().lastScene();
				boolean cond = this.trainModel.checkScenarioConditions(actual);
				if (cond)
					this.trainModel.resetScenario();
			}
		}
		// else if (!trainModel.isStarted()) {
		// trainModel.startTrainingScenario();
		// } else {
		// boolean done = trainModel.evalTrainingScenario(cs);
		// if (done) {
		// trainModel.resetTrainingScenario();
		// }
		// }
	}

	/**
	 * This method determines, if a training-mode is selected. This is stored in
	 * a local field, that is switched "on", if the local trainer was started.
	 * 
	 * @return true, if a trainer exists,
	 */
	public boolean isTrainingMode() {
		return this.trainingMode;
	}

	/**
	 * This method sets the training-mode to the given value!
	 * 
	 * @param b
	 */
	public void setTrainingMode(boolean b) {
		this.trainingMode = b;
	}

    public static void loadWorldModel(ObjectInputStream in) throws ClassNotFoundException, IOException {
        sceneSet = (SceneSet) in.readObject();

    }


    public static void writeWorldModel(ObjectOutputStream out) throws IOException {
        out.writeObject(sceneSet);
    }
}
